////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2010 Gabriel Montagné Láscaris-Comneno and Alberto
//  Brealey-Guzmán.
//
//  Permission is hereby granted, free of charge, to any person obtaining a
//  copy of this software and associated documentation files (the "Software"),
//  to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense,
//  and/or sell copies of the Software, and to permit persons to whom the
//  Software is furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//  DEALINGS IN THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package com.rojored.logging.targets
{

import flash.events.Event;
import flash.events.IEventDispatcher;
import mx.core.IMXMLObject;
import mx.core.mx_internal;
import mx.logging.LogEvent;
import mx.logging.targets.LineFormattedTarget;

use namespace mx_internal;

/**
 *  Simple logging target which will add frame count and stack trace info  to
 *  the basic trace logger.  This is meant to help lifecycle, animation and
 *  race condition bugs and optimizations.
 */
public class FrameCountTarget extends LineFormattedTarget
{

    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private static const CLASS_METHOD_MATCH:RegExp =
        /(at )?\b(?P<classAndMethod>[\w]+\/[: \w]+)\(\)/;

    /**
     *  @private
     */
    private static const LINE_NUMBER_MATCH:RegExp = /:(?P<lineNumber>\d+)]/;

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor
     */
    public function FrameCountTarget()
    {
        super();
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private var frameCount:int = -1;

    //--------------------------------------------------------------------------
    //
    //  Overriden methods:AbstractTarget
    //
    //--------------------------------------------------------------------------

    /**
     *  IMXMLObject initialization
     */
    override public function initialized(document:Object, id:String):void
    {
        super.initialized(document, id);
        if (includeFrameCount)
        {
            var disptacher:IEventDispatcher = document as IEventDispatcher;
            if (disptacher)
                disptacher.addEventListener(
                    Event.ENTER_FRAME,
                    dispatcher_enterFrameHandler
                );
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Overriden methods:LineFormattedTarget
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override mx_internal function internalLog(message:String):void
    {
        if (includeFrameCount)
            message = "[f:" + frameCount + "] " + message;

        if (includeStack)
        {
            var stackTrace:String = new Error().getStackTrace() || "";
            message += ("\n        " +
                stackTrace.split(/\n/).slice(7).map(trimStackElement).join("→ ") +
                "\n");
        }

        trace(message);
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //--------------------------------------
    //  includeFrame
    //--------------------------------------

    /**
     *  if <code>true</code> the frame count will be included in the log
     *
     *  <p>Changing this argument at runtime is not yet supported.</p>
     */
    public var includeFrameCount:Boolean = false;

    //--------------------------------------
    //  includeStack
    //--------------------------------------

    /**
     *  if <code>true</code> a representation of the stack trace will be
     *  attempted.
     */
    public var includeStack:Boolean = false;

    //--------------------------------------
    //  frameCountMax
    //--------------------------------------

    /**
     *  maximum frame count value around which the count will start again.
     */
    public var frameCountMax:int = 1000;

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private function trimStackElement(item:String, index:int,
                                      array:Array):String
    {
        var classAndMethod:String = CLASS_METHOD_MATCH.test(item) ?
            CLASS_METHOD_MATCH.exec(item).classAndMethod.replace(/\//, "·") +
            "()": "(unknown)";

        var lineNumber:String = LINE_NUMBER_MATCH.test(item) ?
            LINE_NUMBER_MATCH.exec(item).lineNumber : "";

        return classAndMethod + (lineNumber ? ":" + lineNumber : "");
    }

    //--------------------------------------------------------------------------
    //
    //  Event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    private function dispatcher_enterFrameHandler(event:Event):void
    {
        frameCount = (frameCount + 1) % frameCountMax
    }
}
}
